package com.mytijian.openapi.adapter.dingding.sevice.impl;


import java.util.List;
import java.util.Optional;
import java.util.UUID;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSON;
import com.mytijian.openapi.adapter.dingding.consts.DingDingConsts;
import com.mytijian.openapi.adapter.dingding.exception.DingDingException;
import com.mytijian.openapi.adapter.dingding.param.DingDingModifyParam;
import com.mytijian.openapi.adapter.dingding.param.DingDingReportParam;
import com.mytijian.openapi.adapter.dingding.param.DingDingReserveConfirmParam;
import com.mytijian.openapi.adapter.dingding.param.DingDingReserveStateParam;
import com.mytijian.openapi.adapter.dingding.param.DingDingStockQueryParam;
import com.mytijian.openapi.adapter.dingding.result.DingDingBasicResult;
import com.mytijian.openapi.adapter.dingding.result.DingDingConfirmResult;
import com.mytijian.openapi.adapter.dingding.result.DingDingCooperationOrderInfo;
import com.mytijian.openapi.adapter.dingding.result.DingDingReportResult;
import com.mytijian.openapi.adapter.dingding.result.DingDingReserveStateResult;
import com.mytijian.openapi.adapter.dingding.result.DingDingStockQueryResult;
import com.mytijian.openapi.adapter.dingding.result.DingDingStorage;
import com.mytijian.openapi.adapter.dingding.result.UserInfo;
import com.mytijian.openapi.adapter.dingding.sevice.DingDingService;
import com.mytijian.openapi.core.client.IOpenAPIClient;
import com.mytijian.openapi.core.client.param.BookParam;
import com.mytijian.openapi.core.client.param.ChangeExamDateParam;
import com.mytijian.openapi.core.client.param.RegisterParam;
import com.mytijian.openapi.core.client.result.BaseResult;
import com.mytijian.openapi.core.client.result.BookResult;
import com.mytijian.openapi.core.client.result.CounterInfo;
import com.mytijian.openapi.core.client.result.CounterResult;
import com.mytijian.openapi.core.client.result.MealInfo;
import com.mytijian.openapi.core.client.result.OrderInfo;
import com.mytijian.openapi.core.client.result.OrderListResult;
import com.mytijian.openapi.core.client.result.PeriodInfo;
import com.mytijian.openapi.core.client.result.RegisterResult;
import com.mytijian.openapi.core.model.AppConfig;
import com.mytijian.openapi.core.service.OpenApiAppConfigService;
import com.mytijian.openapi.core.utils.MD5;
import com.mytijian.utils.Lists;

@Service
public class DingDingServiceImpl implements DingDingService {

	private Logger log = LoggerFactory.getLogger(DingDingServiceImpl.class);
	
	@Autowired
	private OpenApiAppConfigService openApiService;
	
	@Autowired
	private IOpenAPIClient client;
	
	@Override
	public DingDingConfirmResult confirm(DingDingReserveConfirmParam param) {
		String unifyKey = UUID.randomUUID().toString();
		AppConfig config = openApiService.getConfig(DingDingConsts.AppName);
		MealInfo meal = getMealInfo(unifyKey, config, param.getPackageCode());
		String openId = updateUserInfoAndGetOpenId(param, config, unifyKey);
		Integer periodId = getPeriod(meal.getHospitalId(),param.getReserveDate(), 
				param.getReserveDate(), config);
		// 使用开放平台接口下单
		BookResult bookResult = bookOrder(param, config, openId, meal, periodId);
		if(bookResult == null || !StringUtils.isEmpty(bookResult.getErrCode())){
			log.error("*** [钉钉] [下单] 下单失败:{}, param:{}, mealId:{}, periodId:{}",bookResult.getErrMsg(), JSON.toJSON(param), 
					meal.getMealId(), periodId);
			throw new DingDingException("500", "钉钉调用开放平台下单失败");
		}
		DingDingConfirmResult returnResult = new DingDingConfirmResult("200", "体检套餐预定成功", bookResult.getOrderNum());
		return returnResult;
	}

	private Integer getPeriod(Integer hospitalId,  String startDate, String endDate, AppConfig config) {
		CounterResult counterResult = client.getCounter(config, hospitalId, startDate, endDate);
		List<CounterInfo> countInfoList = counterResult.getDayCapacitys();
		if(counterResult == null || CollectionUtils.isEmpty(countInfoList) || 
				CollectionUtils.isEmpty(countInfoList.get(0).getPeriodCapacitys())){
			log.warn("*** [钉钉] [下单] 获取体检时间段为空， hospitalId:{}, examDate:{}", hospitalId, 
					startDate);
			throw new DingDingException("500", "获取体检中心体检时间段为空，无法下单");
		}
		Optional<PeriodInfo> optInfo = countInfoList.get(0).getPeriodCapacitys().stream()
				.filter(info -> info.getUsable()).findFirst();
		Integer periodId = optInfo.get().getPeriod();
		return periodId;
	}

	private String updateUserInfoAndGetOpenId(DingDingReserveConfirmParam param, AppConfig config, String unifyKey) {
		RegisterParam userInfo = new RegisterParam();
		userInfo.setUsername(param.getName());
		userInfo.setIdcard(param.getCertNumber());
		userInfo.setMobile(param.getPhone());
		RegisterResult userResult = client.register(config, userInfo);
		
		if (userResult==null || StringUtils.isEmpty(userResult.getOpenId())) {
			log.error("*** [钉钉] [下单] unifykey:{} 证件号:{} 注册/更新用户信息失败，开发平台返回: {}",unifyKey, 
					param.getCertNumber(), JSON.toJSON(userResult));
			throw new DingDingException("500", "调用开放平台保存用户信息失败");
		}
		return userResult.getOpenId();
	}
	
	private BookResult bookOrder(DingDingReserveConfirmParam param, AppConfig config, 
			String openId, MealInfo meal, Integer periodId) {
		
		BookParam bookParam = new BookParam();
		bookParam.setExamAccountName(MD5.stringMD5(param.getName() + config.getAppSignKey()));
		bookParam.setIdCard(MD5.stringMD5(param.getCertNumber() + config.getAppSignKey()));
		bookParam.setMobile(MD5.stringMD5(param.getPhone() + config.getAppSignKey()));
		
		bookParam.setOrderPrice(meal.getPrice());
		bookParam.setExamTimeIntervalId(periodId);

		bookParam.setMealId(meal.getMealId());
		bookParam.setOpenId(openId);

		bookParam.setExamDate(param.getReserveDate());

		// XXX 对方未给价格
		bookParam.setAgencySellingPrice(meal.getPrice());

		bookParam.setAgencyGoodsId(param.getPackageCode());
		
		BookResult b = client.book(config, bookParam);
		return b;
	}
	
	private MealInfo getMealInfo(String unifyKey, AppConfig config, String goodsId) {
		MealInfo meal = client.getMealByTmallProductId(config, goodsId);

		if (meal == null || !StringUtils.isEmpty(meal.getErrCode())) {
			log.error("*** [钉钉] [下单] unifykey:{} goodsId:{} 未找到该套餐, 错误原因:{}", unifyKey, goodsId, 
					meal != null? meal.getErrMsg():"套餐为空");
			throw new DingDingException("500", String.format("下单失败   goodsId:%s 未查询到可用套餐", goodsId));
		}

		return meal;
	}
	
	@Override
	public DingDingReportResult report(DingDingReportParam param) {
		String orderNum = param.getUniqReserveCode();
		if (orderNum == null) {
			log.error("*** [钉钉] [pdf报告查询] 订单号为空");
			throw new DingDingException("400", "预约唯一码不能为空");
		}
				
		AppConfig config = openApiService.getConfig(DingDingConsts.AppName);
		OrderListResult orderResult = client.getOrderStatus(config, orderNum);
		if(orderResult == null || orderResult.getOrders() == null || 
				orderResult.getOrders().size() > 1){
			log.error("*** [钉钉] [pdf报告查询] 订单异常");
			throw new DingDingException("400", "订单异常");
		}
		String encodedBase64 = client.getPDFExamReport(config, orderNum);
		
		return new DingDingReportResult("200", "体检套餐报告查询成功", encodedBase64);
	}

	@Override
	public DingDingConfirmResult modify(DingDingModifyParam param) {
		String orderNum = param.getUniqReserveCode();
		if (orderNum == null) {
			log.error("*** [钉钉] [订单改期] 订单号为空");
			throw new DingDingException("400", "预约唯一码不能为空");
		}
		if (param.getReserveDate() == null) {
			log.error("*** [钉钉] [订单改期] 预约日期为空");
			throw new DingDingException("400", "预约日期不能为空");
		}
		AppConfig config = openApiService.getConfig(DingDingConsts.AppName);
		OrderListResult orderResult = client.getOrderStatus(config, orderNum);
		if(orderResult == null || orderResult.getOrders() == null || 
				orderResult.getOrders().size() > 1){
			log.error("*** [钉钉] [订单改期] 订单异常");
			throw new DingDingException("400", "订单异常");
		}
		ChangeExamDateParam changeDateParam = new ChangeExamDateParam();
		changeDateParam.setOrderNum(orderNum);
		Integer periodId = getPeriod(orderResult.getOrders().get(0).getHospitalId(),param.getReserveDate(),
				param.getReserveDate(), config);
		changeDateParam.setExamTimeIntervalId(periodId);
		changeDateParam.setNewExamDate(param.getReserveDate());
		BaseResult result = client.changeExamDate(config, changeDateParam);
		if(result == null || !StringUtils.isEmpty(result.getErrCode())){
			log.error("*** [钉钉] [改期] 改期失败:{}, param:{},periodId:{}",result.getErrMsg(), 
					JSON.toJSON(param), periodId);
			throw new DingDingException("500", "钉钉调用开放平台改期失败");
		}
		return new DingDingConfirmResult("200", result.getMsg(), param.getUniqReserveCode());
	}

	@Override
	public DingDingBasicResult cancel(DingDingModifyParam param) {
		String orderNum = param.getUniqReserveCode();
		if (orderNum == null) {
			log.error("*** [钉钉] [订单撤销] 订单号为空");
			throw new DingDingException("400", "预约唯一码不能为空");
		}
		AppConfig config = openApiService.getConfig(DingDingConsts.AppName);
		OrderListResult orderResult = client.getOrderStatus(config, orderNum);
		if(orderResult == null || orderResult.getOrders() == null || 
				orderResult.getOrders().size() > 1){
			log.error("*** [钉钉] [订单撤销] 订单异常");
			throw new DingDingException("400", "订单异常");
		}
		BaseResult result = client.revoke(config, orderNum);
		if(result == null || !StringUtils.isEmpty(result.getErrCode())){
			log.error("*** [钉钉] [撤单] 撤单失败:{}, param:{}",result.getErrMsg(), JSON.toJSON(param));
			throw new DingDingException("500", "钉钉调用开放平台撤单失败");
		}
		return new DingDingBasicResult("200", result.getMsg());
	}

	@Override
	public DingDingStockQueryResult stockQuery(DingDingStockQueryParam param) {
		AppConfig config = openApiService.getConfig(DingDingConsts.AppName);
		CounterResult counterResult = client.getCounter(config, Integer.valueOf(param.getHospitalId()), 
				param.getTimeFrom(), param.getTimeTo());
		List<CounterInfo> counterInfoList = counterResult.getDayCapacitys();
		DingDingStockQueryResult result = new DingDingStockQueryResult();
		if(!CollectionUtils.isEmpty(counterInfoList)){
			List<DingDingStorage> storageList = Lists.newArrayList();
			for(CounterInfo info : counterInfoList){
				DingDingStorage storage = getStorage(info);
				storageList.add(storage);
			}
			result.setStorageList(storageList);
		}
		return result;
	}

	private DingDingStorage getStorage(CounterInfo info) {
		DingDingStorage storage = new DingDingStorage();
		storage.setStrRegdate(info.getDateStr());
		List<PeriodInfo> periodInfos = info.getPeriodCapacitys();
		int maxNum = 0;
		if(!CollectionUtils.isEmpty(periodInfos)){
			for(PeriodInfo pinfo : periodInfos){
				if(pinfo.getUsable()){
					maxNum += pinfo.getAvailableNum();
				}
			}
		}
		storage.setMaxNum(String.valueOf(maxNum));
		storage.setStatus(maxNum == 0? "02" : "00");
		storage.setUsedNum("0");
		return storage;
	}

	@Override
	public DingDingReserveStateResult state(DingDingReserveStateParam param) {
		String orderNum = param.getUniqReserveCode();
		if (orderNum == null) {
			log.error("*** [钉钉] [订单状态查询] 订单号为空");
			throw new DingDingException("400", "预约唯一码不能为空");
		}
		AppConfig config = openApiService.getConfig(DingDingConsts.AppName);
		OrderListResult orderResult = client.getOrderStatus(config, orderNum);
		if(orderResult == null || orderResult.getOrders() == null || 
				orderResult.getOrders().size() > 1){
			log.error("*** [钉钉] [订单状态查询] 订单异常");
			throw new DingDingException("400", "订单异常");
		}
		UserInfo userInfo = client.userInfo(config, orderResult.getOrders().get(0).getOpenId());
		DingDingCooperationOrderInfo cooperationOrderInfo = getReserveState(param, orderResult, userInfo);
		return new DingDingReserveStateResult("200","体检套餐状态查询成功", cooperationOrderInfo);
	}

	private DingDingCooperationOrderInfo getReserveState(DingDingReserveStateParam param, OrderListResult orderResult,
			UserInfo userInfo) {
		DingDingCooperationOrderInfo cooperationOrderInfo = new DingDingCooperationOrderInfo();
		cooperationOrderInfo.setCertNumber(userInfo.getIdCard());
		cooperationOrderInfo.setCertType("0");
		OrderInfo orderInfo = orderResult.getOrders().get(0);
		cooperationOrderInfo.setCheckDate(orderInfo.getExamDate());
		cooperationOrderInfo.setCheckNo(orderInfo.getOrderNum());
		cooperationOrderInfo.setGender(String.valueOf(userInfo.getGender()));
		cooperationOrderInfo.setMarried(String.valueOf(userInfo.getMarriageStatus()));
		cooperationOrderInfo.setName(userInfo.getName());
		cooperationOrderInfo.setPhone(userInfo.getMobile());
		if(orderInfo.getOrderStatus().equals("finish")){
			cooperationOrderInfo.setReportstatus("exam_done");
		} else if(orderInfo.getOrderStatus().equals("unexamine")){
			cooperationOrderInfo.setReportstatus("exam_not");
		} else {
			cooperationOrderInfo.setReportstatus("exam_ing");
		}
		if(orderInfo.getOrderStatus().equals("unexamine")){
			cooperationOrderInfo.setReportstatus("exam_ing");
		}
		cooperationOrderInfo.setReserveNumber(param.getReserveNumber());
		cooperationOrderInfo.setUniqReserveCode(orderInfo.getOrderNum());
		return cooperationOrderInfo;
	}

}
